import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react'
import TitleDom from '@/components/TitleDom'
import { Tree } from '@alifd/next'
import { useMemoizedFn, useWhyDidYouUpdate } from 'ahooks'
import { compare, translate } from '@/utils/tools'
import store from '@/store'
import { v4 as uuidv4 } from 'uuid'
import { isEqual, isEmpty, compact, uniq } from 'lodash-es'
import './index.module.scss'

const TreeDom = ({ initObj, treeType, createKeyTitle, updateBIMName }, ref) => {
  const [graphState, graphDispatchers] = store.useModel('graph')
  const { data: _data, stage, bim: _bim, selectedIds, idToEye, onlyToEye, ugmId, projectData, userType } = graphState

  const [uid] = useState(uuidv4())
  const [data, setData] = useState()
  const [bim, setBIM] = useState()
  const [treeData, setTreeData] = useState()
  const [expandedKeys, setExpandedKeys] = useState([])
  const [checkedKeys, setCheckedKeys] = useState([])
  const [selectedKeys, setSelectedKeys] = useState([])
  const [searchValue, setSearchValue] = useState('')
  const [all, setAll] = useState()
  const [autoExpandParent, setAutoExpandParent] = useState(false)

  const treeRef = useRef()
  const dataListRef = useRef([])
  const lastLayerKeysRef = useRef([])
  const checkedKeysRef = useRef([])
  const lastCheckedKeysRef = useRef([])
  const expandedKeysRef = useRef([])
  const isTypeChangedRef = useRef(false)
  const idToKey = useRef({})
  const keyToId = useRef({})
  const defaultExpandedKeys = useRef([])
  const titleObj = useRef({})
  const fatherAndChildren = useRef({})

  checkedKeysRef.current = [...checkedKeys]
  expandedKeysRef.current = [...expandedKeys]

  // useWhyDidYouUpdate('useWhyDidYouUpdateComponent', {
  //   initObj,
  //   treeType,
  //   createKeyTitle,
  //   updateBIMName,
  //   uid,
  //   data,
  //   bim,
  //   treeData,
  //   expandedKeys,
  //   checkedKeys,
  //   selectedKeys,
  //   searchValue,
  //   idToEye,
  //   onlyToEye,
  //   all,
  //   autoExpandParent,
  //   stage,
  //   selectedIds,
  //   ugmId,
  //   projectData,
  //   userType
  // })

  useEffect(() => {
    if (treeType === 'project') return
    if (!isEmpty(_data)) {
      const newData = { cols: { ..._data.cols }, docs: {} }
      if (stage.includes('concrete')) {
        newData.cols.layer = newData.cols.layer.filter(
          i => i.type === 'Beam' || i.type === 'Column' || i.type === 'MainWall'
        )
      } else {
        newData.cols.layer = newData.cols.layer.filter(i => i.type !== 'Beam' && i.type !== 'Column')
      }
      for (const key in newData.cols) {
        newData.cols[key].forEach(i => {
          newData.docs[i.id] = i
        })
      }
      setData(newData)
    }
  }, [_data, stage, treeType])

  useEffect(() => {
    if (treeType === 'project') return
    if (!isEmpty(_bim)) setBIM(_bim)
  }, [_bim, treeType])

  useEffect(() => {
    if (!bim || isEmpty(bim) || isEmpty(bim.docs)) return
    if (treeType === 'project') return
    let arr = selectedIds
    let newArr = [...arr]
    arr.forEach(id => {
      if (bim.docs[id] && bim.docs[id].data) {
        const data = bim.docs[id].data
        newArr = [...newArr, ...data]
      }
    })
    arr = newArr
    const keys = []
    for (const key in keyToId.current) {
      const id = keyToId.current[key]
      if (arr.includes(id)) {
        keys.push(key)
      }
    }
    const oldKeys = compact(checkedKeysRef.current.filter(i => keyToId.current[i]))
    const sign =
      keys.length > 0 && oldKeys.length > 0 && keys.every(i => oldKeys.includes(i)) && checkKeys(keys, oldKeys)
    if (!sign) setCheckedKeys(keys)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedIds, bim, treeType])

  useEffect(() => {
    handleAll()
    // eslint-disable-next-line no-use-before-define
  }, [treeType, data, bim, projectData, ugmId, handleAll])

  // eslint-disable-next-line react-hooks/exhaustive-deps
  useEffect(() => init(), [all, initObj, idToEye, onlyToEye])

  const handleAll = useMemoizedFn(() => {
    if ((treeType !== 'project' && data && bim) || (treeType === 'project' && projectData)) {
      let newAll = {}
      switch (treeType) {
        case 'entity':
          newAll = {
            ...data.cols,
            layer: data.cols.layer.map(i => {
              return { ...i, entity: ugmId }
            }),
            block: data.cols.block.map(i => {
              return { ...i, entity: ugmId }
            }),
            entity: [{ id: ugmId }]
          }
          break
        case 'bim':
          newAll = {
            ...bim.cols,
            layer: data.cols.layer.map(i => {
              return { ...i, entity: ugmId }
            }),
            block: data.cols.block.map(i => {
              return { ...i, entity: ugmId }
            }),
            entity: [{ id: ugmId }]
          }
          break
        case 'project':
          newAll = { ...projectData.cols }
          break
        default:
      }
      if (!isEqual(newAll, all)) setAll(newAll)
    }
  })

  const setEye = useMemoizedFn(({ ids, treeKey, type }) => {
    let oldIds, oldKeys
    if (type === 'idToEye') {
      oldIds = idToEye.ids
      oldKeys = { ...idToEye.keys }
    } else if (type === 'onlyToEye') {
      oldIds = onlyToEye.ids
      oldKeys = { ...onlyToEye.keys }
    }
    if (!oldKeys[uid]) oldKeys[uid] = []
    if (ids) {
      let newIdToEye = uniq(oldIds)
      ids.forEach(id => {
        if (!newIdToEye.includes(id)) {
          newIdToEye.push(id)
        } else {
          const index = newIdToEye.findIndex(i => i === id)
          newIdToEye[index] = null
        }
      })
      newIdToEye = compact(newIdToEye)
      const newIds = getUnVisibleIds(newIdToEye)
      const newKeys = newIds.map(id => idToKey.current[id])
      return { ids: newIds, keys: { ...oldKeys, [uid]: newKeys } }
    } else {
      const node = dataListRef.current.find(i => i.key === treeKey).node
      const keys = getChildrenKey(node)
      const ids = compact(uniq(keys.map(key => keyToId.current[key])))
      if (!oldKeys[uid].includes(treeKey)) {
        graphDispatchers.update({
          [type]: {
            ids: compact(uniq([...oldIds, ...ids])),
            keys: { ...oldKeys, [uid]: [...oldKeys[uid], ...keys] }
          }
        })
      } else {
        graphDispatchers.update({
          [type]: {
            ids: compact(uniq(oldIds.filter(i => !ids.includes(i)))),
            keys: { ...oldKeys, [uid]: oldKeys[uid].filter(i => !keys.includes(i)) }
          }
        })
      }
    }
  })

  const createTitle = useMemoizedFn(({ name, id, treeKey, textColor }) => {
    return (
      <TitleDom
        name={name}
        id={id}
        idToEye={idToEye}
        treeKey={treeKey}
        treeType={treeType}
        onlyToEye={onlyToEye}
        setEye={setEye}
        textColor={textColor}
        updateBIMName={value => updateBIMName({ value, id })}
      />
    )
  })

  const generateList = useMemoizedFn(data => {
    for (let i = 0; i < data.length; i++) {
      const node = data[i]
      const { key, label } = node
      dataListRef.current.push({ key, label, node })
      if (node.children) {
        fatherAndChildren.current[key] = node.children
        generateList(node.children)
      }
    }
  })

  const init = useMemoizedFn(() => {
    if (all) {
      if (!Object.keys(all).length) return
      let treeData = []
      const _idToKey = {}
      const _keyToId = {}
      defaultExpandedKeys.current = []
      const addDataToTree = ({ treeData, key, lastTreeKey, sign = false, father, label, id }) => {
        const arr = !sign
          ? all[key]
          : all[key].filter(doc => {
              if (doc[father]) {
                if (doc[father] === (label || id)) {
                  return true
                }
              } else if (doc[father]) {
                if (doc[father] === (label || id)) {
                  return true
                }
              }
              return undefined
            })

        let _treeData = []
        lastTreeKey = `${lastTreeKey}-${treeData.length}`
        const treeDataItem = {
          label: createKeyTitle({
            name: key,
            fatherId: id,
            arr,
            treeKey: lastTreeKey,
            idToEye,
            onlyToEye,
            setEye,
            getCheckedKeys: () => checkedKeysRef.current,
            getKeyToId: () => keyToId.current,
            getIdToKey: () => idToKey.current
          }),
          key: lastTreeKey,
          children: [],
          isLeaf: false
        }
        if (treeType === 'project' && id) {
          const definition = projectData.docs[id].definition
          if (definition !== 'storey' && definition !== 'ugm') {
            treeDataItem.isLeaf = false
            treeDataItem.checkable = false
          }
        }
        treeData.push(treeDataItem)
        _treeData = treeData[treeData.length - 1].children

        if (!all[key]) {
          treeData.pop()
          return
        }

        if (arr.length === 0) {
          if (treeType === 'entity') {
            treeData.pop()
          } else {
            treeData[treeData.length - 1].isLeaf = true
            treeData[treeData.length - 1].checkable = false
          }
          return
        } else {
          if (
            arr[0].definition === 'room' ||
            arr[0].definition === 'layer' ||
            arr[0].definition === 'door' ||
            arr[0].definition === 'window' ||
            arr[0].definition === 'mainwall' ||
            arr[0].definition === 'drywall' ||
            arr[0].definition === 'scanstation' ||
            arr[0].definition === 'project' ||
            arr[0].definition === 'apartment' ||
            arr[0].definition === 'installment' ||
            arr[0].definition === 'building' ||
            arr[0].definition === 'storey' ||
            arr[0].definition === 'separator'
          ) {
            arr.sort(compare)
          }
          arr.forEach((doc, index) => {
            const treeKey = `${lastTreeKey}-${index}`
            _idToKey[doc.id] = treeKey
            _keyToId[treeKey] = doc.id
            const _treeDataItem = {
              key: treeKey,
              children: [],
              id: doc.id,
              isLeaf: false
            }
            if (
              titleObj.current[doc.id] &&
              titleObj.current[doc.id].updatedAt &&
              doc.updatedAt &&
              titleObj.current[doc.id].updatedAt === doc.updatedAt &&
              titleObj.current[doc.id].label
            ) {
              _treeDataItem.label = titleObj.current[doc.id].label
            } else {
              _treeDataItem.label = createTitle({ ...doc, treeKey })
              titleObj.current[doc.id] = {
                label: _treeDataItem.label,
                updatedAt: doc.updatedAt
              }
            }
            _treeData.push(_treeDataItem)
          })
        }
        return _treeData
      }

      const initTreeData = (obj, treeData, lastTreeKey = '0', sign = false, father, label, id) => {
        Object.keys(obj).forEach(key => {
          const _treeData = addDataToTree({ treeData, key, lastTreeKey, sign, father, label, id })
          if (Object.keys(obj[key]).length > 0) {
            if (_treeData) {
              _treeData.forEach(treeItem =>
                initTreeData(obj[key], treeItem.children, treeItem.key, true, key, treeItem.name, treeItem.id)
              )
              _treeData.forEach(treeItem => {
                if (treeItem.children && treeItem.children.length === 0) {
                  treeItem.isLeaf = true
                  treeItem.checkable = true
                }
                if (treeType === 'project') {
                  const definition = projectData.docs[treeItem.id].definition
                  if (definition !== 'storey' && definition !== 'ugm') {
                    treeItem.isLeaf = false
                    treeItem.checkable = false
                  }
                }
              })
            }
          } else {
            if (_treeData) {
              for (let i = 0; i < _treeData.length; i++) {
                _treeData[i].isLeaf = true
                _treeData[i].checkable = true
                if (treeType === 'project') {
                  const definition = projectData.docs[_treeData[i].id].definition
                  if (definition !== 'storey' && definition !== 'ugm') {
                    _treeData[i].isLeaf = false
                    _treeData[i].checkable = false
                  } else {
                    if (definition === 'storey' && !projectData.docs[_treeData[i].id].ugm) {
                      // _treeData[i].isLeaf = true
                      // _treeData[i].checkable = false
                    } else if (definition === 'storey' && projectData.docs[_treeData[i].id].ugm) {
                      const ugmId = projectData.docs[_treeData[i].id].ugm
                      const ugm = projectData.docs[ugmId]
                      if (userType === 'staff') {
                        if (ugm.taskState !== '待处理' && ugm.taskState !== '退回修改') {
                          // _treeData[i].isLeaf = true
                          // _treeData[i].checkable = false
                        }
                      } else if (userType === 'auditor') {
                        if (ugm.taskState !== '待审核' && ugm.taskState !== '已审核') {
                          // _treeData[i].isLeaf = true
                          // _treeData[i].checkable = false
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        })
      }
      initTreeData(initObj, treeData)
      idToKey.current = _idToKey
      keyToId.current = _keyToId
      if (isTypeChangedRef.current) {
        setCheckedKeys([...lastLayerKeysRef.current])
        isTypeChangedRef.current = false
      }
      if (treeData[0] && treeData[0].children && treeData[0].children[0]) treeData = treeData[0].children[0].children
      dataListRef.current = []
      generateList(treeData)
      if (treeType === 'project') {
        defaultExpandedKeys.current = dataListRef.current.map(i => i.key)
        setExpandedKeys(defaultExpandedKeys.current)
      }
      setTreeData(treeData)
    }
  }, [all, createTitle, generateList, initObj, treeType, createKeyTitle])

  const onExpand = useMemoizedFn(expandedKeys => {
    setExpandedKeys(expandedKeys)
  })

  const getTrueKeys = keys => {
    const ids = compact(keys.map(key => keyToId.current[key]))
    const newKeys = [...keys]
    for (const key in keyToId.current) {
      if (keyToId.current[key] && ids.includes(keyToId.current[key])) {
        newKeys.push(key)
      }
    }
    return compact(uniq(newKeys))
  }

  const onCheck = useMemoizedFn((checkedKeys, { node }) => {
    if (treeType !== 'project') {
      let newCheckedKeys = getTrueKeys(checkedKeys)
      const deleteKeys = lastCheckedKeysRef.current.filter(key => !newCheckedKeys.includes(key))
      const deleteIds = deleteKeys.map(key => keyToId.current[key])
      newCheckedKeys = newCheckedKeys.filter(key => !deleteIds.includes(keyToId.current[key]))
      lastCheckedKeysRef.current = newCheckedKeys
      const newSelectedIds = compact(uniq(newCheckedKeys.map(key => keyToId.current[key])))
      graphDispatchers.update({ selectedIds: newSelectedIds })
      setCheckedKeys(newCheckedKeys)
    } else {
      let newCheckedKeys
      const eventKey = node.props.eventKey
      const definition = projectData.docs[keyToId.current[eventKey]].definition
      if (definition === 'storey' || definition === 'ugm') {
        // if (checked) {
        //   newCheckedKeys = getTrueKeys([eventKey])
        // } else {
        //   newCheckedKeys = getTrueKeys([])
        // }
        // const deleteKeys = lastCheckedKeysRef.current.filter(key => !newCheckedKeys.includes(key))
        // const deleteIds = deleteKeys.map(key => keyToId.current[key])
        // newCheckedKeys = newCheckedKeys.filter(key => !deleteIds.includes(keyToId.current[key]))
        // lastCheckedKeysRef.current = newCheckedKeys
        // const id = keyToId.current[newCheckedKeys[0]]
        // const newSelectedIds = compact(uniq(newCheckedKeys.map(key => keyToId.current[key])))
        // if (id) {
        //   const newUgmId = definition === 'storey' ? projectData.docs[id].ugm : id
        //   if (newUgmId !== ugmId) {
        //     graphDispatchers.update({ ugmId: newUgmId, selectedStoreyIds: newSelectedIds })
        //   }
        // } else {
        //   graphDispatchers.update({ ugmId: '', selectedStoreyIds: newSelectedIds })
        // }
        // setCheckedKeys(newCheckedKeys)
        newCheckedKeys = getTrueKeys(checkedKeys)
        const newSelectedIds = compact(uniq(newCheckedKeys.map(key => keyToId.current[key])))
        graphDispatchers.update({ selectedStoreyIds: newSelectedIds })
        setCheckedKeys(checkedKeys)
      }
    }
  })

  const getChildrenKey = useMemoizedFn(parentNode => {
    let keys = [parentNode.key]
    if (parentNode.children && parentNode.children.length > 0) {
      for (let i = 0; i < parentNode.children.length; i++) {
        keys = [...keys, ...getChildrenKey(parentNode.children[i])]
      }
    }
    return keys
  })

  const getParentKey = useMemoizedFn((key, tree) => {
    let parentKey
    for (let i = 0; i < tree.length; i++) {
      const node = tree[i]
      if (node.children) {
        if (node.children.some(item => item.key === key)) {
          parentKey = node.key
        } else if (getParentKey(key, node.children)) {
          parentKey = getParentKey(key, node.children)
        }
      }
    }
    return parentKey
  })

  const handleState = useMemoizedFn((label, value) => {
    if (label && label.props && label.props.id) {
      const id = label.props.id
      const doc = projectData.docs[id]
      if (doc.definition === 'ugm') {
        if (value === doc.taskState) {
          return true
        }
      } else if (doc.definition === 'storey' && doc.ugm) {
        const ugm = projectData.docs[doc.ugm]
        if (value === ugm.taskState) {
          return true
        }
      }
      return false
    }
    return false
  })

  const onSearch = useMemoizedFn(value => {
    if (isEmpty(value) || !value) {
      setExpandedKeys(defaultExpandedKeys.current)
      setSearchValue(value)
      setAutoExpandParent(defaultExpandedKeys.current > 0)
    } else {
      const expandedKeys = dataListRef.current
        .map(item => {
          if (treeType === 'project' && handleState(item.label, value)) {
            return getParentKey(item.key, treeData)
          }
          if (item.label.indexOf) {
            if (translate(item.label).indexOf(value) > -1) {
              return getParentKey(item.key, treeData)
            }
          } else {
            if (item.label.props.name && translate(item.label.props.name).indexOf(value) > -1) {
              return getParentKey(item.key, treeData)
            } else if (item.label.props.id && item.label.props.id.indexOf(value) > -1) {
              return getParentKey(item.key, treeData)
            }
          }
          return undefined
        })
        .filter((item, i, self) => item && self.indexOf(item) === i)
      setExpandedKeys(expandedKeys)
      setSearchValue(value)
      setAutoExpandParent(expandedKeys.length > 0)
    }
  })

  const getUnVisibleIds = useMemoizedFn(_idToEye => {
    if (!_idToEye) _idToEye = idToEye.ids
    const unVisibleIds = [..._idToEye]
    const ids = []
    unVisibleIds.forEach(id => {
      if (idToKey.current[id]) {
        Object.keys(keyToId.current).forEach(key => {
          let sign = false
          if (idToKey.current[id].length === key.length) {
            sign = idToKey.current[id] === key
          } else if (idToKey.current[id].length < key.length) {
            sign = idToKey.current[id] + '-' === key.substr(0, idToKey.current[id].length + 1)
          }
          if (sign) ids.push(keyToId.current[key])
        })
      } else {
        ids.push(id)
      }
    })
    return ids
  })

  const checkKeys = useMemoizedFn((keys, oldKeys) => {
    let sign = true
    const headKeys = oldKeys.filter(i => !keys.includes(i))
    const allKeys = Object.keys(keyToId.current)
    if (headKeys.length > 0) {
      for (let i = 0; i < headKeys.length; i++) {
        const headKey = headKeys[i]
        const allHeadKeys = allKeys.filter(o => {
          const _headKey = headKey + '-'
          if (_headKey === o.substr(0, _headKey.length)) {
            return true
          } else {
            return undefined
          }
        })
        const _sign = keys.every(p => allHeadKeys.includes(p))
        if (!_sign) {
          sign = false
          break
        }
      }
    }
    return sign
  })

  const loop = useMemoizedFn(data => {
    if (!data) return
    if (!searchValue) return data
    const taskStates = [
      '待处理',
      '待审核',
      '待识图审核',
      '审核通过',
      '退回修改',
      '规划失败',
      '已上传',
      '待上传',
      '用户驳回修改',
      '待复制规划结果',
      '已识图'
    ]
    if (taskStates.includes(searchValue)) {
      return data.map(item => {
        let label = item.label
        if (handleState(item.label, searchValue)) {
          label = createTitle({ ...item.label.props, textColor: '#f50' })
        }
        return { ...item, label, key: item.key, children: item.children && loop(item.children) }
      })
    } else {
      return data.map(item => {
        const _title = item.label.indexOf
          ? translate(item.label)
          : translate(item.label.props.name) || item.label.props.id
        const index = _title.indexOf(searchValue)
        const beforeStr = _title.substr(0, index)
        const afterStr = _title.substr(index + searchValue.length)
        const label =
          index > -1 ? (
            <span>
              {beforeStr}
              <span style={{ color: '#f50' }}>{searchValue}</span>
              {afterStr}
            </span>
          ) : (
            item.label
          )
        return { ...item, label, key: item.key, children: item.children && loop(item.children) }
      })
    }
  })

  const handleExpandedKeys = useMemoizedFn(expandedKeys => {
    return uniq(expandedKeys).sort(compare)
  })

  const getHeight = useMemoizedFn(expandedKeys => {
    if (treeType !== 'project') {
      let height = 28
      let oldChildren = []
      if (expandedKeys[0] !== '0-0-0-0') return
      expandedKeys.forEach(key => {
        if (oldChildren.length === 0 || oldChildren.includes(key)) {
          height = height + 28 + fatherAndChildren.current[key].length * 28
          oldChildren = [...oldChildren, ...fatherAndChildren.current[key].map(i => i.key)]
        }
      })
      if (height < 900) {
        return height
      } else {
        return
      }
    }
  })

  const onSelect = useMemoizedFn(keys => {
    if (keys.length === 1) {
      const id = keyToId.current[keys[0]]
      const doc = projectData.docs[id]
      if (doc) {
        if (doc.definition === 'storey') {
          if (keys[0] === selectedKeys[0]) {
            setSelectedKeys([])
          } else {
            graphDispatchers.update({ ugmId: doc.ugm })
            setSelectedKeys(keys)
          }
        } else if (doc.definition === 'ugm') {
          if (keys[0] === selectedKeys[0]) {
            setSelectedKeys([])
          } else {
            graphDispatchers.update({ ugmId: id })
            setSelectedKeys(keys)
          }
        }
      }
    } else if (keys.length === 0) {
      setSelectedKeys([])
    }
  })

  useImperativeHandle(ref, () => ({
    onSearch: onSearch,
    setEye: setEye
  }))

  return (
    <Tree
      ref={treeRef}
      style={{ overflow: 'auto', maxHeight: '100%', minHeight: getHeight(handleExpandedKeys(expandedKeys)) }}
      checkable={true}
      showLine
      useVirtual={treeType !== 'project'}
      selectable={treeType === 'project'}
      onSelect={onSelect}
      onExpand={onExpand}
      multiple={false}
      selectedKeys={selectedKeys}
      expandedKeys={handleExpandedKeys(expandedKeys)}
      autoExpandParent={autoExpandParent}
      defaultExpandAll={false}
      onCheck={onCheck}
      checkedKeys={checkedKeys}
      dataSource={loop(treeData)}
    />
  )
}

export default forwardRef(TreeDom)
